package com.acme.bankapp.service.bank;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.util.Properties;
import java.util.regex.Pattern;

import javax.security.auth.login.AccountNotFoundException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.acme.bankapp.account.AbstractAccount;
import com.acme.bankapp.account.NotEnoughFundsException;
import com.acme.bankapp.account.OverDraftLimitExceededException;
import com.acme.bankapp.account.impl.CheckingAccount;
import com.acme.bankapp.account.impl.SavingAccount;
import com.acme.bankapp.domain.bank.Account;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.BankException;
import com.acme.bankapp.domain.bank.client.Client;
import com.acme.bankapp.domain.bank.client.ClientExistsException;
import com.acme.bankapp.domain.bank.client.Gender;

/**
 * Created: Nov 8, 2013 6:22:37 PM
 */

/**
 * @author Andrey Alekov
 *
 */
public class BankService {
	final static Logger logger = LoggerFactory
			.getLogger(BankService.class);
	/** 
	 * Add client to bank.
	 * @param bank to change
	 * @param client to add
	 * @throws ClientExistsException if client already exist in bank
	 */
	public void addClient(Bank bank, Client client) throws ClientExistsException{
		bank.addClient(client);
	}
	
	/** 
	 * Print maximum amount to bank.
	 * @param bank to print
	 */
	public void printMaximumAmountToWithdraw(Bank bank){
		bank.printMaximumAmountToWithdraw();
	}

	/** 
	 * Modify client balance
	 * @throws NotEnoughFundsException if not enough balance to withdraw
	 * @throws OverDraftLimitExceededException if not enough balance and overdraft
	 * @throws BankException if unexpected action performed
	 * @throws AccountNotFoundException accountId for client not found.
	 */
	public void modifyBank(Bank bank, Client client, long accountId, double amount) 
			throws NotEnoughFundsException, OverDraftLimitExceededException, 
			BankException, AccountNotFoundException{
		if (amount >= 0d){
			client.getAccount(accountId).deposit(amount);
		}
		try {
			client.getAccount(accountId).withdraw(amount);
		} catch (OverDraftLimitExceededException e) {
			throw new OverDraftLimitExceededException(client, e.getMessage());
		}
	}

	/** 
	 * Get bank balance
	 * @param bank to print balance.
	 */
	public void printBalance(Bank bank){
		bank.printMaximumAmountToWithdraw();
	}
	
	/**
	 * Save bank state to file.
	 * @param bank to save at file.
	 * @param fileName to save.
	 * @exception FileNotFoundException when file not found.
	 * @exception IOException
	 */
	public void saveBank(Bank bank, String fileName, boolean binaryMode) throws FileNotFoundException, 
		IOException {
		try (FileOutputStream fileStream = new FileOutputStream(fileName)){
			if (binaryMode){
				ObjectOutputStream objectWriter = new ObjectOutputStream(fileStream);
				objectWriter.writeObject(bank);
				objectWriter.close();
			}
			else{
				// TODO: Weakness of format
				//		1. No AccountId cause format are hard coded
				//		2. List of account needs to save separately from clients
				OutputStreamWriter streamWriter = new OutputStreamWriter(fileStream);
				for (Client client : bank.getClients()) {
					for (Account account : client.getAccounts()) {
						String out = String.format("Type=%s|State=%s|Balance=%s|"
								+ "Overdraft=%s|Name=%s|Gender=%s\n",
								account.getAccountType(),
								account.getState(),
								account.getBalance(),
								account.maximumAmountToWithdraw() - account.getBalance(),
								client.getName(),
								client.getGender());
						streamWriter.write(out);
						streamWriter.flush();
					}
				}
				streamWriter.close();
			}
		}
	}
	/**
	 * Read bank state from file.
	 * @param fileName from where reads bank class.
	 * @exception FileNotFoundException when file not found.
	 * @exception IOException
	 * @exception ClassNotFoundException
	 */
	public Bank readBank(String fileName, boolean binaryMode) throws FileNotFoundException, 
		IOException, ClassNotFoundException {
		try (FileInputStream fileStream = new FileInputStream(fileName)){
			ObjectInputStream inputStream = new ObjectInputStream(fileStream);
			Bank bank = (Bank)inputStream.readObject();
			fileStream.close();
			return bank;
		} 
	}

	/**
	 * Read bank state from text file.
	 * @param fileName from where reads bank class.
	 * @exception FileNotFoundException when file not found.
	 * @exception IOException
	 * @exception ClassNotFoundException
	 */
	@SuppressWarnings("deprecation")
	public void readBank(Bank bank, String fileName) throws FileNotFoundException, 
		IOException, ClassNotFoundException {
		String line;
		try (BufferedReader reader = new BufferedReader(new FileReader(fileName))){
			if ((line = reader.readLine()) != null){
				Properties prop = new Properties();
				prop.clear();
				for (String pair :line.split(Pattern.quote("|"))) {
					prop.setProperty(pair.split("=")[0], pair.split("=")[1]);
				}
				Gender gender = Gender.MALE;
				if (prop.getProperty("Gender") == "FEMALE"){
					gender = Gender.FEMALE;
				}
				Client client= new Client(prop.getProperty("Name"), gender);
				try {
					bank.addClient(client);
				} catch (ClientExistsException e) {
					logger.warn("Client " + prop.getProperty("Name") + " alredy added.");
				}
				Account account;
				if (prop.getProperty("Type") == "CHECKING"){
					account = new CheckingAccount(new Double(prop.getProperty("Balance")), 
							new Double(prop.getProperty("Overdraft")));
				}
				else {
					// TODO: Needs to ask clients about format of accounts.
					account = new SavingAccount(new Double(prop.getProperty("Balance")));
				}
				client.addAccount((AbstractAccount) account);
			}
			reader.close();
		} 
	}
}
